from datetime import datetime

from django.db import transaction
from django_redis import get_redis_connection
from decimal import Decimal
from rest_framework import serializers

from goods.models import SKU
from goods.serializers import SkuSerializer
from orders.models import OrderInfo,OrderGoods


class CartSKUSerializer(serializers.ModelSerializer):

    count = serializers.IntegerField(min_value=1,label='数量',read_only=True)

    class Meta:
        model = SKU
        fields = ('__all__')
        extra_kwargs = {
            'caption': {'required': False},
            'price': {'required': False},
            'market_price': {'required': False},
            'name': {'required': False},
            'cost_price': {'required': False},
            'category': {'required': False},
            'goods': {'required': False},
        }


class OrderSettlementSerializer(serializers.Serializer):
    """
    订单结算数据序列化器
    """
    freight = serializers.DecimalField(label='运费',max_digits=10,decimal_places=2)
    skus = CartSKUSerializer(many=True)
    # skus = SkuSerializer(many=True)




class SaveOrderSerializer(serializers.ModelSerializer):
    # 使用CreateAPIView如果序列化器继承ModelSerializer就可以不用再在序列化器写create方法，
    # 会自动增加一个该模型类的一个对象，create方法返回的对象根据序列化器有的字段序列化返回给前端
    # 如果使用Serializers就要自己写，返回的对象也会返回给序列化给前端，
    class Meta:
        model = OrderInfo
        fields = ('order_id','address','pay_method')
        extra_kwargs = {
            'address':{
                'write_only':True
            },
            'pay_method':{
                'write_only':True
            },
            'order_id':{
                'read_only':True
            }
        }

    def create(self, validated_data):
        # 获取数据:
        #   获取user,address,pay_method参数,组建order_id
        # 数据库操作:
        #   1.把数据库操作放在事务下
        #   2.用传来的数据新增一个订单信息对象
        #   3.从redis中获取选中的sku_id，count
        #   4.把选中的sku_id和count组建成一个字典cart
        #   5.遍历cart字典的键列表，把每次遍历出来的sku_id查询出sku对象
        #   6.获取购买数量和库存，判断库存是否比数量少，是就抛出错误
        #   7.如果库存足够就在库存中减少要购买的数量和销量上增加购买数量
        #   8.进行修改数据的时候判断库存和销量是否还是原来的库存和销量，看更新语句返回影响的行数
        #   9.如果影响的行数是等于０就表明库存和销量修改过，就要结束本次循环重新获取新的库存再去修改
        #  10.如果影响的行数不等０就修改成功，再修改商品的总销量，改变订单信息对象的数量和总价格
        #  11.新增订单商品对象，订单信息对象的总价格加上运费，保存修改数据，退出循环
        #  12.如果数据库操作中有异常就回滚，
        #　删除redis:
        #　不然就删除掉redis中的选中状态的sku_id数据，返回order对象

        # 1.获取当前下单用户
        user = self.context['request'].user

        # 2.生成订单编号
        order_id = datetime.now().strftime('%Y%m%d%H%M%S') + '%09d' % user.id
        # order_id = datetime.now().strftime('%Y%m%r%H%M%S')+ '%09d'%user.id

        address = validated_data['address']
        pay_method = validated_data['pay_method']

        with transaction.atomic():
            # 生成保存节点
            save_point = transaction.savepoint()

            # 3.保存订单基本信息数据 OrederInfo
            try:
                order = OrderInfo.objects.create(order_id=order_id,
                                                 user=user,
                                                 address=address,
                                                 total_count=0,
                                                 total_amount=Decimal(0),
                                                 freight=Decimal(10),
                                                 pay_method=pay_method,
                                                 status=OrderInfo.ORDER_STATUS_ENUM.get('UNPAID') if
                                                 pay_method==2 else OrderInfo.ORDER_STATUS_ENUM.get('UNSEND'),
                                                 )

                # 4.从redis中获取购物车结算商品数据
                conn = get_redis_connection('cart')
                # 获取选中状态的sku_id
                sku_list = conn.smembers('carts_selected_%s'%user.id)
                cart_dict = conn.hgetall('carts_%s'%user.id)
                cart = {}
                for sku_id in sku_list:
                    print(sku_id)
                    cart[int(sku_id)] = int(cart_dict[sku_id])
                # 5.遍历结算商品
                for sku_id in cart.keys():
                    while True:
                        sku = SKU.objects.get(id=sku_id)
                        print(sku_id)

                        sku_count = cart[sku_id]

                        old_stock = sku.stock # 原始库存
                        old_sales = sku.sales # 原始销量
                        # 判断商品库存
                        if sku_count > old_stock:
                            raise serializers.ValidationError('库存不足')
                        # 减少商品库存，增加商品销量
                        new_stock = old_stock - sku_count
                        new_sales = old_sales + sku_count
                        # 保存订单商品数据, 使用乐观锁
                        ret = SKU.objects.filter(id=sku_id,stock=old_stock,sales=old_sales).update(stock=new_stock,sales=new_sales)
                        # 别的用户可能已经更改了old_stock，所以修改失败，
                        # 修改失败continue再次修改ＳＫＵ表
                        if ret == 0:
                            continue
                        # 还需要修改该品牌的总销量
                        sku.goods.sales += sku_count
                        order.total_count += sku_count
                        order.total_amount += (sku_count * sku.price)

                        # 保存数据到orderGoods订单商品表
                        OrderGoods.objects.create(order=order,
                                                  sku=sku,
                                                  count=sku_count,
                                                  price=sku.price,
                                                  )
                        break
                    # 每次遍历商品都要加上每件sku商品的运费
                    order.total_amount += order.freight
                    order.save()

            except:
                transaction.savepoint_rollback(save_point)

            else:
                transaction.savepoint_commit(save_point)

                pl = conn.pipeline()
                # 成功保存所有数据后在redis购物车中删除已计算商品数据
                pl.hdel('carts_%s'%user.id,*sku_list)
                pl.srem('carts_selected_%s'%user.id,*sku_list)
                pl.execute()

                return order

























